/*
 * Copyright (C) 2005 Johan Maasing johan at zoom.nu Licensed under the Apache
 * License, Version 2.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
 * or agreed to in writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package nu.zoom.swing.desktop.plugin.laf;

import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.ButtonGroup;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

import nu.zoom.swing.desktop.PlugIn;
import nu.zoom.swing.desktop.Workbench;
import nu.zoom.swing.desktop.WorkbenchListener;
import nu.zoom.swing.desktop.common.BackendException;
import nu.zoom.swing.desktop.preferences.Preferences;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ops4j.gaderian.Messages;

import com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel;

/**
 * A plug in that can change the look and feel of the desktop.
 * 
 * @author Johan Maasing
 */
@SuppressWarnings("restriction")
public class LookAndFeelManager implements PlugIn, WorkbenchListener {
	private Log log = LogFactory.getLog(getClass());

	private static final String PREFERRED_LAF_PREFS_KEY = "LookAndFeel";

	private Preferences preferences;

	// private HashMap looks = new HashMap();
	private Workbench workbench;

	private Messages messages;

	private ArrayList<DesktopLookAndFeel> availableLAFs = new ArrayList<DesktopLookAndFeel>();

	private List<DesktopLookAndFeel> lookAndFeelServices;

	public LookAndFeelManager(Workbench workbench, Messages messages, Preferences preferences,
			List<DesktopLookAndFeel> lookAndFeelServices) {
		super();
		this.workbench = workbench;
		this.messages = messages;
		this.preferences = preferences;
		this.lookAndFeelServices = lookAndFeelServices;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nu.zoom.swing.desktop.PlugIn#initialize()
	 */
	@SuppressWarnings("unchecked")
	public void initialize() throws Exception {
		String preferredLAFName = null;
		try {
			preferredLAFName = preferences.getString(PREFERRED_LAF_PREFS_KEY);
		} catch (Exception e) {
		}

		// Get the installed LAFs (the JDK defaults)
		UIManager.LookAndFeelInfo[] installedLAFsInfo = UIManager.getInstalledLookAndFeels();
		for (int i = 0; i < installedLAFsInfo.length; i++) {
			String classname = installedLAFsInfo[i].getClassName();
			log.trace("Trying to load Installed LAF class:" + classname);
			Class<LookAndFeel> lafClass = (Class<LookAndFeel>) getClass().getClassLoader().loadClass(classname);
			LookAndFeel laf = (LookAndFeel) lafClass.newInstance();
			if (laf.isSupportedLookAndFeel()) {
				log.trace("LAF reported it was supported by the platform");
				// JDK 1.5 defines this, but names it exactly the same
				// as the default windows so you do not know which one you are
				// using.
				if (!(laf instanceof WindowsClassicLookAndFeel)) {
					log.trace("LAf was not WindowsClassicLookAndFeel, adding LAF");
					availableLAFs.add(new InstalledLookAndFeel(laf));
				}
			}
		}

		// Get the HiveMind configured LAFs
		for (DesktopLookAndFeel laf : lookAndFeelServices) {
			if (laf.isAvailable()) {
				log.trace("LAF " + laf.getName() + " is available");
				availableLAFs.add(laf);
			}
		}

		// Set the preferred LAF
		if (preferredLAFName != null) {
			log.trace("Trying to set the preferred LAF to: " + preferredLAFName);
			for (DesktopLookAndFeel laf : availableLAFs) {
				if (preferredLAFName.equals(laf.getName())) {
					try {
						laf.initialize();
					} catch (UnsupportedLookAndFeelException exc) {
						log.warn("Preferred LAF could not be set, trying fallback LAF");
						try {
							UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
						} catch (Exception exc1) {
							log.fatal("Tried to fallback to cross platform LAF but failed", exc1);
							System.err.println("Tried to fallback to cross platform LAF but failed");
							exc1.printStackTrace();
						}
					}
				}
			}
		}

		workbench.addWorkBenchListener(this);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nu.zoom.swing.desktop.WorkbenchListener#close()
	 */
	public void close() {
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see nu.zoom.swing.desktop.WorkbenchListener#start()
	 */
	public void start() {
		if (availableLAFs.size() > 0) {
			String preferredLAFName = null;
			try {
				preferredLAFName = preferences.getString(PREFERRED_LAF_PREFS_KEY);
				if (log.isTraceEnabled()) {
					log.trace("Preferred LAF from preferences: " + preferredLAFName);
				}
			} catch (Exception e) {
			}

			if (preferredLAFName == null) {
				// User has no preferences for LAF
				preferredLAFName = UIManager.getLookAndFeel().getName();
			}

			log.trace("Creating LAF menu");
			JMenu lafMenu = new JMenu(messages.getMessage("menu.laf"));
			lafMenu.setMnemonic(KeyEvent.VK_L);
			lafMenu.setToolTipText(messages.getMessage("menu.laf.tooltip"));
			ButtonGroup lafGroup = new ButtonGroup();
			for (int i = 0; i < availableLAFs.size(); i++) {
				DesktopLookAndFeel laf = availableLAFs.get(i);
				JRadioButtonMenuItem lafMenuItem = new JRadioButtonMenuItem(new ActivateLookAndFeelAction(this, laf));
				lafGroup.add(lafMenuItem);
				lafMenu.add(lafMenuItem);
				if (preferredLAFName != null) {
					if (preferredLAFName.equals(laf.getName())) {
						lafMenuItem.setSelected(true);
					}
				}
			}
			boolean separatorDrawn = false;
			for (int i = 0; i < availableLAFs.size(); i++) {
				if (availableLAFs.get(i) instanceof ConfigurableDesktopLookAndFeel) {
					if (!separatorDrawn) {
						lafMenu.addSeparator();
						separatorDrawn = true;
					}
					ConfigurableDesktopLookAndFeel claf = (ConfigurableDesktopLookAndFeel) availableLAFs.get(i);
					ConfigureLookAndFeelAction action = new ConfigureLookAndFeelAction(workbench, messages, claf);
					lafMenu.add(action.getControl());
				}
			}
			workbench.getMenuBar().addToApplicationMenu(lafMenu);
		}
	}

	public void changeLookAndFeel(DesktopLookAndFeel desktopLAF) {
		if (log.isTraceEnabled()) {
			log.trace("Trying to store preferred LAF: " + desktopLAF.getName() + " in preferences");
		}
		try {
			preferences.setString(PREFERRED_LAF_PREFS_KEY, desktopLAF.getName());
		} catch (BackendException e) {
			log.error("Unable to store preferred LAF", e);
			workbench.getErrorReporter().reportError(messages.format("error.preferences", PREFERRED_LAF_PREFS_KEY), e);
		}
		try {
			log.trace("Calling UIManager.setLookAndFeel");
			desktopLAF.initialize();
			// These two might cause flickering
			// (http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5079688)
			JFrame.setDefaultLookAndFeelDecorated(true);
			JDialog.setDefaultLookAndFeelDecorated(true);
			SwingUtilities.updateComponentTreeUI(workbench.getDialogOwner());
		} catch (UnsupportedLookAndFeelException exc) {
			log.warn("Unable to change LAF, fallback to cross platform LAF", exc);
			try {
				UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
			} catch (Exception exc1) {
				log.error("Tried to fallback to cross platform LAF but failed", exc1);
				System.err.println("Tried to fallback to cross platform LAF but failed");
				exc1.printStackTrace();
			}
			workbench.getErrorReporter().reportError(messages.getMessage("unsupportedlaf"), exc);
		}
	}

	@Override
	public void visible() {
	}

}
